/*
 * Copyright (C) 2011-2013 Eric Lasota
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#ifndef __RDX_THREADING_HPP__
#define __RDX_THREADING_HPP__

#include "rdx_basictypes.hpp"
//#include "rdx_objectmanagement.hpp"

namespace RDX
{
	struct OperationContext;

	namespace ObjectManagement
	{
		template<class _GCT> class CRef;
		struct IObjectManager;
	};

	namespace Threading
	{
		struct IMutex
		{
			virtual bool TryAcquire(int msec) = 0;
			virtual void Acquire(int msec) = 0;
			virtual void Release() = 0;
			virtual void Destroy() = 0;

			inline void Acquire()
			{
				this->Acquire(0);
			}
		};

		struct ISystemThreadProc
		{
			virtual void Start() const = 0;
		};

		struct ISystemThread
		{
			virtual ~ISystemThread() = 0;
			virtual bool WaitFor(int msec = -1) = 0;
			virtual bool IsActive() = 0;
		};

		int CountProcessors();
		ObjectManagement::CRef<IMutex> CreateMutex(OperationContext *ctx, ObjectManagement::IObjectManager *objm);
		void DestroyMutex(IMutex *mtx);
		ISystemThread *CreateSystemThread(const ISystemThreadProc *proc);
		void DestroySystemThread(ISystemThread *sysThread);
		void SleepThread(LargeInt msec);

		NonAtomicInt AtomicIncrement(volatile AtomicInt *v);
		NonAtomicInt AtomicDecrement(volatile AtomicInt *v);
		NonAtomicInt NonAtomicDecrement(volatile AtomicInt *v);
		NonAtomicInt AtomicRead(const volatile AtomicInt *v);
		NonAtomicInt NonAtomicRead(const volatile AtomicInt *v);
		void AtomicWrite(volatile AtomicInt *v, NonAtomicInt value);
		void NonAtomicWrite(volatile AtomicInt *v, NonAtomicInt value);
		// Compares *exchangeAddr to compareTo, and if equal, sets it to replacement.  Returns the original value of *exchangeAddr
		NonAtomicInt AtomicCompareExchange(volatile AtomicInt *exchangeAddr, NonAtomicInt replacement, NonAtomicInt compareTo);

		inline NonAtomicInt AtomicIncrement(NonAtomicInt *v) { return ++(*v); }
		inline NonAtomicInt AtomicDecrement(NonAtomicInt *v) { return --(*v); }
		inline NonAtomicInt NonAtomicDecrement(NonAtomicInt *v) { return --(*v); }
		inline NonAtomicInt AtomicRead(const NonAtomicInt *v) { return *v; }
		inline NonAtomicInt NonAtomicRead(const NonAtomicInt *v) { return *v; }
		inline void AtomicWrite(NonAtomicInt *v, NonAtomicInt value) { *v = value; }
		inline void NonAtomicWrite(NonAtomicInt *v, NonAtomicInt value) { *v = value; }

		void Synchronize();

		class LightMutex
		{
			volatile AtomicInt _i;

		public:
			inline LightMutex()
			{
				NonAtomicWrite(&_i, 0);
			}

			inline bool TryAcquire(int count = 1)
			{
				while(count--)
				{
					if(AtomicCompareExchange(&_i, 1, 0) != 0)
					{
						Threading::Synchronize();
						return true;
					}
				}
				return false;
			}

			inline void Acquire()
			{
				while(AtomicCompareExchange(&_i, 1, 0) == 0)
				{
				}
				Threading::Synchronize();
			}

			inline void Release()
			{
				AtomicWrite(&_i, 0);
			}
		};

		template<class _Tmutex>
		class MutexLock
		{
			_Tmutex *_mtx;

		public:
			inline explicit MutexLock(_Tmutex *mtx)
			{
				_mtx = mtx;
				_mtx->Acquire();
			}

			inline ~MutexLock()
			{
				_mtx->Release();
			}
		};
	};
};

#endif
